package com.dhcc.bpm.alarm.utils;

import com.alibaba.druid.filter.config.ConfigTools;
import com.alibaba.fastjson.JSONObject;
import com.dhcc.bpm.alarm.config.ConfigToolConfig;
import com.dhcc.bpm.alarm.entity.AlarmFtpInfoConfig;
import com.jcraft.jsch.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springblade.core.tool.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.*;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static java.lang.String.format;

/**
 * Created by hidden on 2018/8/12.
 * author zhangzexu
 * update guogang 修改定时任务info日志级别为debug
 */
@Component
public class SSHExecutor {
    private static Logger LOGGER = LoggerFactory.getLogger(SSHExecutor.class);
    private static final int SESSION_TIMEOUT = 1000*30;
    private static final int CHANNEL_TIMEOUT = 1000*60*3;
    private static final int TIME_OUT = 1000*60*3;
    private static final int TYPE_KEY = 1;
    private static final int TYPE_NO_PRI = 0;
    private static final String PROXY_STATUS_ON = "1";
    private static final String PROXY_STATUS_OFF = "0";

    @Autowired
    RedisUtil redisUtils;

    @Autowired
    private ConfigToolConfig configToolConfigs;


    private static ConfigToolConfig configToolConfig;
    private ExecutorService executorService = Executors.newCachedThreadPool();


    @PostConstruct
    public void init (){
        configToolConfig = this.configToolConfigs;
    }

    public SSHExecutor(){
    }

    /**
     * 设置自动关闭session的时间,默认六分钟
     * @param timeOut
     * @return
     */
    public void closeSession(Integer timeOut,JSONObject json) {
        executorService.execute(() -> {
            try {
                Thread.sleep(timeOut);
            } catch (InterruptedException e) {
            }
            if(((Session)json.get("session"))!=null) {
                if(((Session)json.get("session")).isConnected()) {
                    LOGGER.info("session超时关闭!");
                    ((Session)json.get("session")).disconnect();
                }
            }
        });
    }
    public JSONObject getSSHSession(AlarmFtpInfoConfig ftpInfoConfig) {
        String ip = ftpInfoConfig.getFtpIp();
        String user = ftpInfoConfig.getFtpUser();
        String password = ftpInfoConfig.getFtpPswd();
        Integer port = Integer.parseInt(ftpInfoConfig.getFtpPort());
        String privateKeys = ftpInfoConfig.getFtpPrikey();
        Integer type = 0;
        if(!StringUtils.isBlank(privateKeys)){
            type = 1;
        }
        String proxyStatus = ftpInfoConfig.getProxyStatus();
        String proxyIp = ftpInfoConfig.getProxyIp();
        Integer proxyPort = ftpInfoConfig.getProxyPort();
        return this.getSSHSession(user, password, ip, port, privateKeys, type,ftpInfoConfig.getFtpTimeout().intValue(),proxyStatus,proxyIp,proxyPort);
    }


    public JSONObject getSSHSession(String user, String password, String ip, Integer port , String privateKeys, Integer type, String proxyStatus, String proxyIp, Integer proxyPort) {
        JSONObject json =  this.getSSHSession(user, password, ip, port, privateKeys, type,TIME_OUT,proxyStatus,proxyIp,proxyPort);
    	return json;
    }
    public JSONObject getSSHSession(String user, String password, String ip, Integer port , String privateKeys) {
        JSONObject json =  this.getSSHSession(user, password, ip, port, privateKeys, TYPE_KEY,PROXY_STATUS_OFF,null,null);
        return json;
    }
    public JSONObject getSSHSession(String user, String password, String ip, Integer port ) {
        JSONObject json =  this.getSSHSession(user, password, ip, port, null, TYPE_NO_PRI,PROXY_STATUS_OFF,null,null);
        return json;
    }
    public JSONObject getSSHSession(String user, String password, String ip, Integer port , String privateKeys,Integer timeOut) {
        JSONObject json =  this.getSSHSession(user, password, ip, port, privateKeys, TYPE_KEY,timeOut,PROXY_STATUS_OFF,null,null);
        return json;
    }
    public JSONObject getSSHSession(String user, String password, String ip, Integer port ,Integer timeOut) {
        JSONObject json =  this.getSSHSession(user, password, ip, port, null, TYPE_NO_PRI,timeOut,PROXY_STATUS_OFF,null,null);
        return json;
    }
    /**
     * 判断连接方式，获取ssh Session连接 传入密钥和登录类型  0 为普通方式  1为密钥方式
     * @param user
     * @param password
     * @param ip
     * @param port
     * @param privateKeys
     * @param type
     * @return
     */
    public JSONObject getSSHSession(String user, String password, String ip, Integer port , String privateKeys, Integer type,Integer timeOut,String proxyStatus, String proxyIp, Integer proxyPort) {
     	JSONObject json = new JSONObject();
     	//密码解密
        if (StringUtils.isNotBlank(password)) {
            try {
                password = ConfigTools.decrypt(configToolConfig.getPublicKey(),password);
            } catch (Exception e) {
                LOGGER.error(ip + "-" + user + "用户密码解密失败");
            }
        }
    	if(type==0) {
    		json = this.getSSHSession(user, password, ip, port,proxyStatus,proxyIp,proxyPort);
    		closeSession(timeOut,json);
    	}else if(type==1) {
    		if(!StringUtils.isBlank(privateKeys)) {
//    		    //秘钥解密
//                try {
//                    password = ConfigTools.decrypt(configToolConfig.getPublicKey(),password);
//                } catch (Exception e) {
//                    LOGGER.error(ip + "-" + user + "用户秘钥解密失败");
//                }
    			json = this.getSSHSession(user, password, ip, port, privateKeys,proxyStatus,proxyIp,proxyPort);
    			closeSession(timeOut,json);
    		}else {
    			LOGGER.error("密钥为空，登陆失败");
    			json.put("message","密钥为空，登陆失败");
                json.put("code",false);
                json.put("session",null);
    		}
    	}else {
    		 LOGGER.error("登录方式为空，登陆失败");
    		 json.put("message","登录方式为空，登陆失败");
             json.put("code",false); 
             json.put("session",null);
    	}
        json.put("timeout",timeOut);
    	return json;
    }
    
    /**
     * 获取ssh Session连接
     * @param user
     * @param password
     * @param ip
     * @param privateKeys
     * @return
     */
    private JSONObject getSSHSession(String user,String password,String ip,Integer port,String privateKeys, String proxyStatus, String proxyIp, Integer proxyPort) {
        SSHInfo sshInfo = new SSHInfo(user, password, ip, port);
        JSONObject json = new JSONObject();
        JSch jSch = new JSch();
        Session session = null;
        String caseName = ip+":"+port+":"+user;
        json.put("caseName",caseName);

        RandomAccessFile randomAccessFile = null;
        File file  =  null;
        try {
            String fileName = System.currentTimeMillis()+UUID.randomUUID().toString()+".crt";
            file = new File(fileName);
            randomAccessFile = new RandomAccessFile(fileName, "rw");
            randomAccessFile.writeBytes(privateKeys);
            if(randomAccessFile!=null){
                try {
                    randomAccessFile.close();
                }catch (Exception e){
                    LOGGER.error("文件流关闭失败",e);
                }
            }
            session = jSch.getSession(sshInfo.getUser(), sshInfo.getHost(), sshInfo.getPort());
            if(StringUtils.isNotBlank(sshInfo.getPassword())){
                jSch.addIdentity(fileName,sshInfo.getPassword());
                session.setPassword(sshInfo.getPassword());
                session.setUserInfo(new MyUserInfo(sshInfo.getPassword()));
            }else {
                jSch.addIdentity(fileName);
                session.setUserInfo(new MyUserInfo("test"));
            }

            if("1".equals(proxyStatus)){
                ProxyHTTP proxyhttp = new ProxyHTTP(proxyIp,proxyPort);
                session.setProxy(proxyhttp);
            }
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
//            JSch登录sftp，跳过 Kerberos username 身份验证提示
//            config.put("PreferredAuthentications","publickey,keyboard-interactive,password");
            session.setConfig(config);
            session.connect(SESSION_TIMEOUT);
            LOGGER.debug("ssh连接成功");
            json.put("message","session连接成功");
            json.put("code",true);
            json.put("session",session);
            return json;
        }catch (Exception e){
            LOGGER.error("ssh连接失败,错误信息:",e);
            if(e.getMessage().contains("Auth"))    //信息提示有歧义，已修正  guogang 20191021
            {
                json.put("message", "用户名或密码错误");
            }else if(e.getMessage().contains("ProxyHTTP")&&e.getMessage().contains("Connection refused")){
                json.put("message","sehll agent 连接失败\n");
            }else if("1".equals(proxyStatus)&&e.getMessage().contains("connection is closed by foreign host")){
                json.put("message","sehll agent 连接被拒绝\n");
            }else {
                json.put("message",e.getMessage());
            }
            json.put("code",false);
            json.put("session",null);
            return json;
        }finally {
            if(file!=null){
                try {
                    file.delete();
                }catch (Exception e){
                    LOGGER.error("文件删除失败",e);
                }
            }
        }

    }





    /**
     * 获取ssh Session连接
     * @param user
     * @param password
     * @param ip
     * @param port
     * @return
     */
    private JSONObject getSSHSession(String user,String password,String ip,Integer port, String proxyStatus, String proxyIp, Integer proxyPort){
        SSHInfo sshInfo = new SSHInfo(user, password, ip, port);
        JSONObject json = new JSONObject();
        JSch jSch = new JSch();
        Session session = null;
        String caseName = ip+":"+port+":"+user;
        json.put("caseName",caseName);
        Long times = 0L;
        try {
            session = jSch.getSession(sshInfo.getUser(), sshInfo.getHost(), sshInfo.getPort());
            session.setPassword(sshInfo.getPassword());
            session.setUserInfo(new MyUserInfo(sshInfo.getPassword()));
            if("1".equals(proxyStatus)){
                ProxyHTTP proxyhttp = new ProxyHTTP(proxyIp,proxyPort);
                session.setProxy(proxyhttp);
            }
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
//          JSch登录sftp，跳过 Kerberos username 身份验证提示
//          config.put("PreferredAuthentications","publickey,keyboard-interactive,password");
            session.setConfig(config);
            session.connect(SESSION_TIMEOUT);
            json.put("message","session连接成功");
            json.put("code",true);
            json.put("session",session);
            return json;
        }catch (Exception e){
            LOGGER.error("ssh连接失败,错误信息:",e);
            if(e.getMessage().contains("Auth"))    //信息提示有歧义，已修正  guogang 20191021
            {
                json.put("message", "用户名或密码错误");
            }else if(e.getMessage().contains("ProxyHTTP")&&e.getMessage().contains("Connection refused")){
                json.put("message","sehll agent 连接失败\n");
            }else if("1".equals(proxyStatus)&&e.getMessage().contains("connection is closed by foreign host")){
                json.put("message","sehll agent 连接被拒绝\n");
            }else {
                json.put("message",e.getMessage());
            }
            json.put("code",false);
            json.put("session",null);
        }
        return json;
    }

    
    public JSONObject exec(JSONObject sessionJson,String cmd){
        return this.exec(sessionJson,cmd,false);
    }
    
    /**
     * @param sessionJson  ssh Session
     * @param cmd  执行的命令
     * @param flag 是否关闭session  true 关闭  false不关闭
     * @return
     */
    public JSONObject exec(JSONObject sessionJson,String cmd,Boolean flag) {

        JSONObject json = new JSONObject();
        json.put("caseName", sessionJson.getString("caseName"));
        Session session =(Session)sessionJson.get("session");
        if(session==null){
            json.put("message",sessionJson.getString("message"));
            json.put("code",false);
            return json;
        }
        if(!session.isConnected()){
            json.put("message","session 连接已断开");
            json.put("code",false);
            return json;
        }
        PrintStream ps = null;
        ChannelExec channelExec = null;
        InputStream in = null;
        File file = null;
        FileInputStream fileInputStream = null;
        try {
            LOGGER.info("shell命令: " + cmd);
            file = new File(BaseUtils.getSystemDateStringY2NO());
            channelExec = (ChannelExec) session.openChannel("exec");
            channelExec.setCommand(cmd);
            ps=new PrintStream(file);
            channelExec.setInputStream(null);
            channelExec.setErrStream(ps);
            in = channelExec.getInputStream();
            channelExec.connect(sessionJson.getIntValue("timeout"));
            int res = -1;
            StringBuffer buf = new StringBuffer(1024);
            byte[] tmp = new byte[1024];
            while (true){
                while (in.available() > 0) {
                    int i = in.read(tmp, 0, 1024);
                    if (i < 0) {
                        break;
                    }
                    buf.append(new String(tmp, 0, i));
                }
                if (channelExec.isClosed()) {
                    res = channelExec.getExitStatus();
                    if(res!=0){
                        LOGGER.error(format("Exit-status: %d", res));
                    }else {
                        LOGGER.info(format("Exit-status: %d", res));
                    }
                    break;
                }
                TimeUnit.MICROSECONDS.sleep(50);
            }
            if(res==0){
                json.put("code",true);
            }else {
                fileInputStream = new FileInputStream(file);
                while (fileInputStream.available() > 0) {
                    int i = fileInputStream.read(tmp, 0, 1024);
                    if (i < 0) {
                        break;
                    }
                    buf.append(new String(tmp, 0, i));
                }
                json.put("code",false);
            }
            json.put("message", buf.toString());
        } catch (Exception e) {
            json.put("message", e.getMessage());
            json.put("code",false);
        }finally {
            try{
                if(fileInputStream!=null) {
                    fileInputStream.close();
                }
                if(in!=null) {
                    in.close();
                }
                if(ps!=null) {
                    ps.close();
                }
                if(file!=null){
                    file.delete();
                }

            }catch (Exception e){
                LOGGER.error("文件流关闭错误:{}"+e.getMessage());
            }
            channelExec.disconnect();
            if(flag){
                close(session);
            }
        }
        return json;
    }


    /**
     * 关闭session连接
     * @param session
     */
    public void close(Session session) {
        if (session != null) {
            session.disconnect();
        }
    }

    public void close(JSONObject sessionJson){
        Session session =(Session)sessionJson.get("session");
        if(session!=null){
            session.disconnect();
        }

    }
    //false 是关闭 true 是连接
    public boolean isConnected(JSONObject sessionJson){
        Session session =(Session)sessionJson.get("session");
        if(session!=null) {
            return session.isConnected();
        } else {
            return false;
        }
    }
    /*
     * SSH连接信息
     * */
    private class SSHInfo {
        private String user;
        private String password;
        private String host;
        private int port;
        public SSHInfo(String user, String password, String host, int port) {
            this.user = user;
            this.password = password;
            this.host = host;
            this.port = port;
        }
        public String getUser() {
            return user;
        }

        public String getPassword() {
            return password;
        }

        public String getHost() {
            return host;
        }

        public int getPort() {
            return port;
        }
    }

    /*
     * 自定义UserInfo
     * */
    private class MyUserInfo implements UserInfo {

        private String passphrase = null;

        @Override
        public String getPassphrase() {
            return null;
        }

        public MyUserInfo(String passphrase) {
            this.passphrase = passphrase;
        }

        @Override
        public String getPassword() {
            return null;
        }

        @Override
        public boolean promptPassword(String s) {
            return false;
        }

        @Override
        public boolean promptPassphrase(String s) {
            return false;
        }

        @Override
        public boolean promptYesNo(String s) {
            return true;
        }
        @Override
        public void showMessage(String s) {
        }
    }

}
